package com.rad.net.protocol.android.http;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.GZIPInputStream;

import android.content.Context;
import android.text.TextUtils;

import com.rad.net.protocol.android.utils.LogUtils;
import com.rad.net.protocol.android.utils.NetUtils;

/**
 * 类说明：通用联网器基础类，此类提供了与服务器交换数据的功能
 * 用法：构造一个{@link BaseHttpTransporter}对象，通过{@link #sendRequest(Request)}
 * 方法，提交{@link Request}请求对象，从服务器返回{@link Response}响应对象
 * 
 * @author kalui.huang
 * @date 2014年8月18日
 * @version 1.0
 */
public class BaseHttpTransporter implements HttpTransporter {
	private static final String TAG = BaseHttpTransporter.class.getSimpleName();
	private HttpURLConnection mHttpConn;
	private Context mContext;
	/**
	 * 请求次数
	 */
	private int mRequestTimes = 3;
	private int mTimeOut = 60 * 1000; 

	private enum HttpMethod {
		GET("GET"), POST("POST"), ERROR(null);

		private String value;

		HttpMethod(String value) {
			this.value = value;
		}

		public String getMethod() {
			return value;
		}
	}

	/**
	 * 构建一个带上下文的传输对象
	 * @param context 
	 */
	public BaseHttpTransporter(Context context) {
		mContext = context;
	}

	/**
	 * 通过{@link Request}请求对象进行联网请求获取{@link Response}响应对象
	 * 
	 * @param r
	 *            请求对象
	 * @return respond 响应对象
	 */
	@Override
	public Response sendRequest(Request r) {
		Response response = null;
		// 如果r=null，直接返回response=null;
		HttpMethod method = getRequestMethod(r);
		if (method != HttpMethod.ERROR) {
			response = getHttpResponse(r, method);
		}
		return response;
	}

	/**
	 * 由请求对象判断请求方式
	 * 
	 * @param r
	 *            请求对象
	 * @return 正确时返回{@link HttpMethod#GET}或{@link HttpMethod#POST}，错误时返回{@link HttpMethod#ERROR}
	 */
	private HttpMethod getRequestMethod(Request r) {
		// 请求对象为null时返回错误
		if (r == null || TextUtils.isEmpty(r.getUrl())) {
			return HttpMethod.ERROR;
		}
		if (r.getBody() != null) {
			return HttpMethod.POST;
		} else {
			return HttpMethod.GET;
		}
	}

	/**
	 * 获取{@link HttpURLConnection}对象
	 * 
	 * @param requestUrl 请求的url，可能包含GET方式请求参数
	 * @throws Exception
	 */
	private void getHttpConnection(String requestUrl) throws Exception {
		URL url = new URL(requestUrl);
		if (NetUtils.isCmwap(mContext)) {
			mHttpConn = NetUtils.getCmwapConnect(requestUrl);
			LogUtils.i(TAG,"cmwap connection...");
		} else {
			mHttpConn = (HttpURLConnection) url.openConnection();
		}
	}

	/**
	 * 获取输入流对象
	 * 
	 * @param header 响应头
	 * @return inputStream 输入流对象
	 * @throws Exception
	 */
	private InputStream getHttpInputStream(Map<String, String> header) throws Exception {
		InputStream is = mHttpConn.getInputStream();
		String gZipValue = header.get("content-encoding");
		//如果是压缩文件输入流
		if (gZipValue != null && gZipValue.indexOf("gzip") >= 0) {
			LogUtils.i(TAG, "inputStream is gzip...");
			is = new GZIPInputStream(is);
		}
		return is;
	}

	/**
	 * 为{@link HttpURLConnection}设置请求参数
	 * @param request 请求对象
	 * @param requestMethod 请求方法
	 * @throws Exception
	 */
	private void setConnectionProperty(Request request, HttpMethod requestMethod) throws Exception {
		mHttpConn.setRequestMethod(requestMethod.getMethod());
		mHttpConn.setConnectTimeout(mTimeOut);
		mHttpConn.setReadTimeout(mTimeOut);
		// 301,302时重定向
		mHttpConn.setInstanceFollowRedirects(true);

		if (HttpMethod.POST == requestMethod) {
			mHttpConn.setDoOutput(true);
			mHttpConn.setDoInput(true);
			mHttpConn.setUseCaches(false);
		}

		// 设置header
		setRequestHeader(request.getHeader());
	}

	/**
	 * http请求，获取响应对象
	 * @param request 请求对象
	 * @param requestMethod 请求方法
	 * @return response 响应对象
	 */
	private Response getHttpResponse(Request request, HttpMethod requestMethod) {
		Response response = new Response();
		try {
			int retryTimes = 0;
			while (retryTimes < mRequestTimes) {
				getHttpConnection(request.getUrl());
				setConnectionProperty(request, requestMethod);
				if (HttpMethod.POST == requestMethod) {
					DataOutputStream dos = new DataOutputStream(mHttpConn.getOutputStream());
					writeOutPostBody(dos, request.getBody());
				}
				Map<String, String> header = getHttpResponseHeader();
				// 确定是自己的服务器下发的内容
				// 有可能在使用CMWAP的时候出现移动服务器下发错误的内容
				if (!isTargetServer(header)) {
					if(HttpMethod.POST == requestMethod){
						break;
					}
					retryTimes++;
					continue;
				}
				response.setHeader(header);
				// 获取响应状态码
				int statusCode = mHttpConn.getResponseCode();
				response.setStatusCode(statusCode);
				InputStream is = getHttpInputStream(header);
				response.setInputStream(is);
				return response;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 *通过post请求方式交换数据时， 将request请求对象的body写入到httpConnection对象的输出流中进行输出，
	 * <b>子类可重写此方法进行传输进度的处理</b>
	 * @param dos
	 *            输出流对象，通过{@link HttpURLConnection#getOutputStream()}方式获取
	 * @param ins
	 *            输入流对象，{@link Request}对象的body,通过
	 *            {@link Request#getBody()}方式获取
	 * @throws IOException
	 */
	protected void writeOutPostBody(OutputStream dos, InputStream ins) throws IOException{
		byte[] buffer = new byte[1024 * 100];
		int length = 0;
		while ((length = ins.read(buffer)) != -1) {
			dos.write(buffer, 0, length);
		}
		ins.close();
		dos.flush();
		dos.close();
	}

	/**
	 * 设置httpConnection连接对象的header</br>
	 * @param header
	 */
	private void setRequestHeader(Map<String, String> header) {
		if (mHttpConn != null && header != null) {
			for (Entry<String, String> entry : header.entrySet()) {
				if (entry.getKey() != null) {
					mHttpConn.setRequestProperty(entry.getKey().toLowerCase(Locale.CHINA), entry.getValue());
				}
			}
		}
	}

	/**
	 *  获取联网请求返回的header
	 * @return 响应的header
	 */
	private Map<String, String> getHttpResponseHeader() {
		Map<String, String> header = new LinkedHashMap<String, String>();
		for (int i = 0;; i++) {
			String field = mHttpConn.getHeaderField(i);
			if (field == null)
				break;
			String key = mHttpConn.getHeaderFieldKey(i);
			if (key != null) {
				// 无论key是大写还是小写，统一转为小写，这样header就不会重复设置，不会出现大小写不同的两个key
				header.put(key.toLowerCase(Locale.CHINA), field);
			}
		}
		return header;
	}

	/**
	 * 由响应的header判断连接的是否是目标服务器，
	 * 如果返回不是目标服务器的数据，则会重试，
	 * 重试次数由{@link #setRequestTimes(int)}决定，
	 * <b>子类可重写判断规则</b>
	 * @param header
	 *               请求之后返回的response包含的header
	 * @return 基类默认返回true
	 */
	protected boolean isTargetServer(Map<String, String> header) {
		return true;
	}

    /**
	 * 设置连接超时时间
	 * 基类默认time为<b>1</b>分钟
	 * @param time
	 */
	public void setTimeOut(int time) {
		mTimeOut = time;
	}

	/**
	 * 设置请求次数，
	 * 其中包含一次正常请求和<b>requestTimes -1</b> 次重试，
	 * 基类默认requestTimes为<b>3</b>次
	 * @param requestTimes
	 */
	public void setRequestTimes(int requestTimes) {
		mRequestTimes = requestTimes;
	}

}
